package com.zoran.study.spring.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.util.concurrent.TimeUnit;

/**
 * RestTemplateUtils
 *
 * @author zouxm
 * @date 2018/1/9
 */
@Slf4j
public class RestTemplateUtils {

    private static RestTemplate restTemplate;

    static {
        /*SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setReadTimeout(30000);
        requestFactory.setConnectTimeout(30000);*/
        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
        requestFactory.setReadTimeout(30000);
        requestFactory.setConnectTimeout(30000);
        HttpClient httpClient = HttpClients.custom()
                .setRedirectStrategy(new LaxRedirectStrategy())
                .evictIdleConnections(60, TimeUnit.SECONDS)
                .evictExpiredConnections()
                //禁用重试策略
                .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))
                .build();
        requestFactory.setHttpClient(httpClient);
        restTemplate = new RestTemplate(requestFactory);
        log.info("RestTemplate init completed ...");
    }

    public static <T> T postForObject(String url, Object request, Class<T> responseType) {
        if (StringUtils.isEmpty(url) || request == null || responseType == null) {
            throw new IllegalArgumentException("argument invalid");
        }
        try {
            T response = restTemplate.postForObject(url, request, responseType);
            return response;
        } catch (Exception ex) {
            log.error("url {},request {}", url, request);
            throw new RuntimeException("http invoke exception", ex);
        }

    }

    public static <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables) {
        if (StringUtils.isEmpty(url) || request == null || responseType == null || uriVariables == null || uriVariables.length == 0) {
            throw new IllegalArgumentException("argument invalid");
        }
        try {
            return restTemplate.postForObject(url, request, responseType, uriVariables);
        } catch (Exception ex) {
            log.error("url {},uriVariables {},request {}", url, uriVariables, request);
            throw new RuntimeException("http invoke exception", ex);
        }
    }

    public static <T> T postForObject(String url, Object request, HttpHeaders headers, Class<T> responseType) {
        if (StringUtils.isEmpty(url) || request == null || responseType == null) {
            throw new IllegalArgumentException("argument invalid");
        }
        try {
            HttpEntity requestEntity = new HttpEntity(request, headers);
            return restTemplate.postForObject(url, requestEntity, responseType);
        } catch (Exception ex) {
            log.error("url {},request {}", url, request);
            throw new RuntimeException("http invoke exception", ex);
        }

    }

    public static <T> T postWithForm(String url, MultiValueMap<String,String> formParams, Class<T> responseType) {
        if (StringUtils.isEmpty(url) || formParams == null || responseType == null) {
            throw new IllegalArgumentException("argument invalid");
        }
        try {
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
            HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formParams, headers);
            return restTemplate.postForObject(url, requestEntity, responseType);
        } catch (Exception ex) {
            log.error("url {},formParams {}", url, formParams);
            throw new RuntimeException("http invoke exception", ex);
        }
    }

    public static <T> T postWithMultipartForm(String url, MultiValueMap<String,String> formParams, Class<T> responseType) {
        if (StringUtils.isEmpty(url) || formParams == null || responseType == null) {
            throw new IllegalArgumentException("argument invalid");
        }
        try {
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.MULTIPART_FORM_DATA);
            HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formParams, headers);
            return restTemplate.postForObject(url, requestEntity, responseType);
        } catch (Exception ex) {
            log.error("url {},formParams {}", url, formParams);
            throw new RuntimeException("http invoke exception", ex);
        }
    }

    public static <T> T postWithForm(String url, MultiValueMap formParams, HttpHeaders headers, Class<T> responseType) {
        if (StringUtils.isEmpty(url) || formParams == null || responseType == null) {
            throw new IllegalArgumentException("argument invalid");
        }
        MediaType requestContentType = headers.getContentType();
        if (!(requestContentType.equals(MediaType.APPLICATION_FORM_URLENCODED)
                || requestContentType.equals(MediaType.MULTIPART_FORM_DATA))) {
            throw new IllegalArgumentException("content type must [application/x-www-form-urlencoded] or [multipart/form-data]");
        }
        try {
            HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formParams, headers);
            return restTemplate.postForObject(url, requestEntity, responseType);
        } catch (Exception ex) {
            log.error("url {},formParams {}", url, formParams);
            throw new RuntimeException("http invoke exception", ex);
        }
    }

    public static <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables) {
        if (StringUtils.isEmpty(url) || request == null || responseType == null || uriVariables == null || uriVariables.length == 0) {
            throw new IllegalArgumentException("argument invalid");
        }
        try {
            return restTemplate.postForEntity(url, request, responseType, uriVariables);
        } catch (Exception ex) {
            log.error("url {},uriVariables {},request {}", url, uriVariables, request);
            throw new RuntimeException("http invoke exception", ex);
        }
    }


    public static <T> T getForObject(String url, Class<T> responseType) {
        if (StringUtils.isEmpty(url) || responseType == null) {
            throw new IllegalArgumentException("argument invalid");
        }
        try {
            return restTemplate.getForObject(url, responseType);
        } catch (Exception ex) {
            log.error("url {}", url);
            throw new RuntimeException("http invoke exception", ex);
        }

    }

    public static <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType) {
        if (StringUtils.isEmpty(url) || responseType == null) {
            throw new IllegalArgumentException("argument invalid");
        }
        try {
            return restTemplate.getForEntity(url, responseType);
        } catch (Exception ex) {
            log.error("url {}", url);
            throw new RuntimeException("http invoke exception", ex);
        }
    }

    private RestTemplateUtils() {
    }

    public static RestTemplate getRestTemplate() {
        return restTemplate;
    }

}
